# -*- coding:utf-8 -*-
# 导包
import json
import jsonpath
import allure
import traceback
from unit_tools.handle_data.yaml_handler import read_yaml, write_yaml
import re
from unit_tools.debugtalk import DebugTalk
from unit_tools.handle_data.configParse import ConfigParse
from unit_tools.sendrequests import SendRequests
from unit_tools.assertion_utils import Assertions
from unit_tools.log_util.recordlog import logs


class RequestsBase:

    def __init__(self):
        self.conf = ConfigParse()
        self.send_request = SendRequests()
        self.asserts = Assertions()

    def parse_and_replace_variables(self, yml_data):
        yml_data_str = yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_ascii=False)
        for _ in range(yml_data_str.count("${")):
            if '${' in yml_data_str and '}' in yml_data_str:
                strat_index = yml_data_str.index("${")
                end_index = yml_data_str.index("}", strat_index)
                variable_data = yml_data_str[strat_index:end_index + 1]
                match = re.match(r'\$\{(\w+)\((.*?)\)\}', variable_data)
                if match:
                    func_name, func_params = match.groups()
                    func_params = func_params.split(',') if func_params else []
                    extract_data = getattr(DebugTalk(), func_name)(*func_params)
                    yml_data_str = re.sub(re.escape(variable_data), str(extract_data), yml_data_str)
        try:
            data = json.loads(yml_data_str)
        except json.JSONDecodeError:
            data = yml_data_str
        return data

    @classmethod
    def allure_attach_dict_result(cls, result):
        if isinstance(result, dict):
            allure_response = json.dumps(result, ensure_ascii=False, indent=4)
        else:
            allure_response = result
        return allure_response

    def execute_test_cases(self, base_info, testcase):
        try:
            conf_host = self.conf.get_host('host')
            url = conf_host + base_info['url']
            api_name = base_info['api_name']
            method = base_info['method']
            headers = base_info.get('headers', None)
            if headers is not None:
                headers = eval(self.parse_and_replace_variables(headers)) if isinstance(headers, str) else headers
            cookies = base_info.get('cookies', None)
            if cookies is not None:
                cookies = eval(self.parse_and_replace_variables(cookies)) if isinstance(cookies, str) else cookies
            case_name = testcase.pop('case_name')
            val_result = self.parse_and_replace_variables(testcase.get('validation'))
            testcase['validation'] = val_result
            validation = testcase.pop('validation')
            extract, extract_list = testcase.pop('extract', None), testcase.pop('extract_list', None)
            param_type, request_params = None, None
            for param_type, param_value in testcase.items():
                if param_type in ['params', 'data', 'json']:
                    request_params = self.parse_and_replace_variables(param_value)
                    testcase[param_type] = request_params
            files = testcase.pop('files', None)
            if files:
                for fk, fv in files.items():
                    files = {fk: open(fv, mode='rb')}
            response = self.send_request.execute_api_request(api_name=api_name, url=url, method=method, headers=headers,
                                                             case_name=case_name, cookies=cookies, files=files,
                                                             **testcase)
            status_code, response_text = response.status_code, response.text
            logs.info(f'接口实际返回结果：{response_text}')
            allure_info = {
                '接口地址': url,
                '接口名称': api_name,
                '请求方式': method,
                '请求头': self.allure_attach_dict_result(headers if headers else '无需请求头'),
                'Cookies': self.allure_attach_dict_result(cookies if cookies else '无需Cookies'),
                '测试用例名称': case_name,
                '参数类型': param_type if param_type else '无需参数类型',
                '请求参数': self.allure_attach_dict_result(request_params if request_params else '无需请求参数入参'),
                '接口实际响应信息': self.allure_attach_dict_result(response.json())
            }
            for title, content in allure_info.items():
                allure.attach(content, title, attachment_type=allure.attachment_type.JSON)
            if extract is not None:
                self.extract_data(extract, response_text)
            if extract_list is not None:
                self.extract_data_list(extract_list, response_text)
            self.asserts.assert_result(validation, response.json(), status_code)
        except Exception as e:
            logs.error(f'出现未知异常：--{str(traceback.format_exc())}')
            raise e

    @classmethod
    def extract_data(cls, testcase_extract, response_text):
        extract_data = None
        try:
            for key, value in testcase_extract.items():
                if any(pat in value for pat in ['(.*?)', '(.+?)', r'(\d+)', r'(\d*)']):
                    ext_list = re.search(value, response_text)
                    extract_data = {key: int(ext_list.group(1)) if r'(\d+)' in value else ext_list.group(1)}
                elif '$' in value:
                    extract_json = jsonpath.jsonpath(json.loads(response_text), value)[0]
                    extract_data = {key: extract_json} if extract_json else {
                        key: "未提取到数据，请检查接口返回信息或表达式！"}
                if extract_data:
                    write_yaml(extract_data)
        except re.error:
            logs.error('正则表达式解析错误，请检查yaml文件extract表达式是否正确！')
        except json.JSONDecodeError:
            logs.error('JSON解析错误，请检查yaml文件extract表达式是否正确！')

    @classmethod
    def extract_data_list(cls, testcase_extract_list, response_text):
        extract_data = None
        try:
            for key, value in testcase_extract_list.items():
                if any(pat in value for pat in ['(.*?)', '(.+?)', r'(\d+)', r'(\d*)']):
                    ext_list = re.findall(value, response_text, re.S)
                    if ext_list:
                        extract_data = {key: ext_list}
                elif "$" in value:
                    extract_json = jsonpath.jsonpath(json.loads(response_text), value)
                    if extract_json:
                        extract_data = {key: extract_json}
                    else:
                        extract_data = {key: '未提取到数据，请检查接口返回信息或表达式！'}
                if extract_data:
                    write_yaml(extract_data)
        except re.error:
            logs.error('正则表达式解析错误，请检查yaml文件extract表达式是否正确！')
        except json.JSONDecodeError:
            logs.error('JSON解析错误，请检查yaml文件extract表达式是否正确！')


# 调试查看
if __name__ == '__main__':
    api_info = read_yaml('.././testcase/userManager/adduser.yaml')
    print(api_info)
    req = RequestsBase()
    req.execute_test_cases(api_info[0][0], api_info[0][1])